package org.arch_learn.servlet_container;

import org.arch_learn.servlet_container.components.Server;
import org.arch_learn.servlet_container.components.Service;
import org.arch_learn.servlet_container.components.container.Context;
import org.arch_learn.servlet_container.components.container.Engine;
import org.arch_learn.servlet_container.components.container.Host;
import org.arch_learn.servlet_container.components.container.Wrapper;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import javax.servlet.http.HttpServlet;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.regex.Pattern;

/**
 * 启动主类
 */
public class JerryRat {
    private Server server;

    public Server getServer() {
        return server;
    }

    public JerryRat() {
        this.server = new Server();
        initJerryRat(server);
    }

    public static void main(String[] args) {
        JerryRat jerryRat = new JerryRat();
        try {
            jerryRat.run();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void run() throws IOException {
        server.getServices().forEach(v -> {
            //多服务循环开启。每一个或者多个服务服务于一个webapp
            v.start();
            System.out.println("==========>>>jerryRat start on port " + v.getPort());
        });
    }

    //解析配置，核心是加载servlet
    private void initJerryRat(Server server) {
        //解析jerryRat配置
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            Element serverElement = rootElement.element("server");

            List<Element> serviceNodes = serverElement.elements("service");
            List<Service> services = new ArrayList<>();
            serviceNodes.forEach(v -> {
                Element executor = v.element("executor");
                //初始化全局线程池
                String corePoolSize = executor.attributeValue("corePoolSize");
                String maximumPoolSize = executor.attributeValue("maximumPoolSize");
                String keepAliveTime = executor.attributeValue("keepAliveTime");
                String blockingQueueCapacity = executor.attributeValue("blockingQueueCapacity");
                ArrayBlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable>(Integer.valueOf(blockingQueueCapacity));
                ThreadFactory threadFactory = Executors.defaultThreadFactory();
                RejectedExecutionHandler abortPolicy = new ThreadPoolExecutor.AbortPolicy();
                ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(Integer.valueOf(corePoolSize), Integer.valueOf(maximumPoolSize), Long.valueOf(keepAliveTime), TimeUnit.SECONDS, blockingQueue, threadFactory, abortPolicy);

                String name = v.attributeValue("name");

                Element connectorNode = v.element("connector");
                String port = connectorNode.attributeValue("port");


                Element engineNode = v.element("engine");
                Element engineNameNode = engineNode.element("name");
                String engineName = engineNameNode.getStringValue();
                Engine engine = new Engine(engineName);
                List<Element> hostNodes = engineNode.elements("host");
                hostNodes.forEach(h -> {
                    String hostValue = h.attributeValue("value");
                    String appBase = h.attributeValue("appBase");
                    Host host = new Host(hostValue, appBase);
                    SAXReader appSaxReader = new SAXReader();
                    try {
                        InputStream appResourceAsStream = new FileInputStream(appBase + "/WEB-INF/web.xml");
                        Document appConfigDoc = appSaxReader.read(appResourceAsStream);
                        Element appRootElement = appConfigDoc.getRootElement();

                        List<Element> contextNodes = appRootElement.elements("context");
                        contextNodes.forEach(contextN -> {
                            String contextPath = contextN.attributeValue("path");
                            String contextName = contextN.attributeValue("name");
                            Context context = new Context(contextName, contextPath);
                            List<Element> servletNodes = contextN.elements("servlet");
                            servletNodes.forEach(servletN -> {

                                Node servletNameNode = servletN.selectSingleNode("servlet-name");
                                String servletName = servletNameNode.getStringValue();
                                Node servletClassNode = servletN.selectSingleNode("servlet-class");
                                String servletClass = servletClassNode.getStringValue();
                                Node urlPatternNode = contextN.selectSingleNode("/web-app/context/servlet-mapping[servlet-name='" + servletName + "']");
                                String urlPattern = urlPatternNode.selectSingleNode("url-pattern").getStringValue();
                                try {
                                    String path = "file:///" + appBase + "/WEB-INF/classes/";

                                    URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL(path)}, Thread.currentThread().getContextClassLoader());
                                    Class<?> aClass = urlClassLoader.loadClass(servletClass);
                                    Object servletInstance = aClass.newInstance();
                                    Wrapper wrapper = new Wrapper(servletName, Pattern.compile(urlPattern), (HttpServlet) servletInstance);
                                    context.getWrappers().add(wrapper);
                                } catch (ClassNotFoundException e) {
                                    e.printStackTrace();
                                } catch (InstantiationException e) {
                                    e.printStackTrace();
                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                } catch (MalformedURLException e) {
                                    e.printStackTrace();
                                }
                            });
                            if (!context.getWrappers().isEmpty()) {
                                host.getContexts().put(contextPath, context);
                            }
                        });
                    } catch (DocumentException e) {
                        e.printStackTrace();
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }

                    if (!host.getContexts().isEmpty()) {
                        engine.getHosts().put(hostValue, host);
                    }
                });
                Service service = new Service(server, name, port, engine, threadPoolExecutor);
                services.add(service);
                server.setServices(services);
            });
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }
}
